﻿using System.Collections.Generic;
using NthDownload.Contracts;
using System.Threading.Tasks;
using System.ComponentModel.Composition.Hosting;
using NthDownload.Composition;
using System.ComponentModel.Composition;
using NthDownload.Contracts.Plugins;
using NthDownload.Contracts.Plugins.Downloads;

namespace NthDownload.Common.Plugins
{
    [PartCreationPolicy(CreationPolicy.Shared)]
    [Export(typeof(DownloadPluginDirectoryService))]
    public class DownloadPluginDirectoryService : IPluginService
    {
        #region Class Members

        private readonly object _syncObject = new object();

        #endregion Class Members

        #region Properties

        /// <summary>
        /// Indicates if the service is currently busy
        /// </summary>
        public bool IsBusy { get; private set; }

        #endregion Properties

        #region Private Properties

        /// <summary>
        /// The catalogue used to discover download plugins
        /// </summary>
        private DirectoryCatalog Catalogue { get; set; }

        #endregion Private Properties

        #region Methods

        /// <summary>
        /// Loads plugins form the download plugins folder
        /// </summary>
        public Task LoadPlugins()
        {
            Task task = new Task(() =>
            {
                //
                // Check/Set busy status
                //
                lock (_syncObject)
                {
                    if (IsBusy)
                    {
                        return;
                    }

                    IsBusy = true;
                }

                //
                // If there is an existing instance of the catalogue call a refresh instead
                //
                if (Catalogue != null)
                {
                    Catalogue.Refresh();

                    //
                    // Register plugin components
                    //
                    RegisterPluginComponents();

                    return;
                }

                //
                // Create catalogue & add to import service
                //
                Catalogue = new DirectoryCatalog(Paths.DownloadPluginsPath);
                ImportService.AddCatalogueToContainer(Catalogue);

                //
                // Register plugin components
                //
                RegisterPluginComponents();

                //
                // Set busy status
                //
                lock (_syncObject)
                {
                    IsBusy = false;
                }
            });

            task.Start();

            return task;
        }

        /// <summary>
        /// Refreshes the plugins from the download plugins folder
        /// </summary>
        public Task RefreshPlugins()
        {
            Task task = new Task(() =>
            {
                //
                // If there is an existing instance of the catalogue call a refresh instead
                //
                if (Catalogue != null)
                {
                    Catalogue.Refresh();

                    //
                    // Register plugin components
                    //
                    RegisterPluginComponents();
                }
            });

            task.Start();

            return task;
        }

        #endregion Methods

        #region Private Methods

        /// <summary>
        /// Registers the download factory for each plugin
        /// </summary>
        private void RegisterPluginComponents()
        {
            //
            // Get loaded download plugins
            //
            IEnumerable<IDownloadPlugin> downloadPlugins = ImportService.GetExportValues<IDownloadPlugin>();

            //
            // Register the download factory from each plugin
            //
            foreach (IDownloadPlugin downloadPlugin in downloadPlugins)
            {
                //
                // Call plugin initialize
                //
                Task initializeTask = downloadPlugin.Initialize();
                initializeTask.Wait();

                string downloadFactoryContract = DownloadPluginContractNameService.CreateDownloadFactoryContractName(downloadPlugin.GUID);

                //
                // If factory isn't registered then register it
                //
                IDownloadFactory downloadFactory;
                if (!ImportService.TryGetExportValue(downloadFactoryContract, out downloadFactory))
                {
                    downloadFactory = downloadPlugin.CreateDownloadFactory();
                    ImportService.AddExportedValueToContainer(downloadFactoryContract, downloadFactory);
                }

                string downloadUIServiceContract = DownloadPluginContractNameService.CreateDownloadUIServiceContractName(downloadPlugin.GUID);

                //
                // If UIService isn't registered then register it
                //
                IDownloadUIService downloadUIService;
                if (!ImportService.TryGetExportValue(downloadUIServiceContract, out downloadUIService))
                {
                    downloadUIService = downloadPlugin.CreateDownloadUIService(downloadFactory);
                    ImportService.AddExportedValueToContainer(downloadUIServiceContract, downloadUIService);
                }

                string downloadXmlSerializationServiceContract = DownloadPluginContractNameService.CreateDownloadXmlSerializationServiceContractName(downloadPlugin.GUID);

                //
                // If serialization service isn't registered then register it
                //
                IDownloadXmlSerializationService downloadXmlSerializationService;
                if (!ImportService.TryGetExportValue(downloadXmlSerializationServiceContract, out downloadXmlSerializationService))
                {
                    downloadXmlSerializationService = downloadPlugin.CreateDownloadXmlSerializationService();
                    ImportService.AddExportedValueToContainer(downloadXmlSerializationServiceContract, downloadXmlSerializationService);
                }
            }
        }

        #endregion Private Methods
    }
}
